package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.zzyl.constant.CacheConstants;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.ResourceDto;
import com.zzyl.entity.Resource;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.mapper.RoleResourceMapper;
import com.zzyl.service.ResourceService;
import com.zzyl.utils.EmptyUtil;
import com.zzyl.utils.NoProcessing;
import com.zzyl.utils.StringUtils;
import com.zzyl.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;

    /**
     * 多条件列表查询
     * @param resourceDto
     * @return
     */
    @Cacheable(cacheNames = CacheConstants.LIST,key = "#resourceDto.hashCode()")
    @Override
    public List<ResourceVo> getList(ResourceDto resourceDto) {
        List<Resource> resources = resourceMapper.selectList(resourceDto);
        return BeanUtil.copyToList(resources,ResourceVo.class);
    }

    /**
     * 封装资源的树形结构
     *
     * @param resourceDto
     * @return
     */
    @Cacheable(cacheNames = CacheConstants.TREE)
    @Override
    public TreeVo resourceTreeVo(ResourceDto resourceDto) {

        //根节点的编号：100001000000000
        //根据根节点的编号，查询出来所有的子孙节点，好处：只查一次数据库
        ResourceDto dto = ResourceDto.builder()
                .parentResourceNo(NoProcessing.processString(SuperConstant.ROOT_PARENT_ID))
                .resourceType(SuperConstant.MENU)
                .dataState(SuperConstant.DATA_STATE_0)
                .build();
        //所有的菜单数据,可以认为，表中的所有数据
        List<Resource> resources = resourceMapper.selectList(dto);

        Resource resource = new Resource();
        resource.setResourceNo(SuperConstant.ROOT_PARENT_ID);
        resource.setResourceName("养老院");

        List<TreeItemVo> itemVos = new ArrayList<>();

        recursionTreeItem(itemVos,resource,resources);

        return TreeVo.builder().items(itemVos).build();
    }

    /**
     * 递归调用封装数据
     * @param itemVos
     * @param root
     * @param resources  所有的数据
     */
    private void recursionTreeItem(List<TreeItemVo> itemVos, Resource root, List<Resource> resources) {

        TreeItemVo treeItemVo = TreeItemVo.builder().id(root.getParentResourceNo()).label(root.getResourceName()).build();

        List<Resource> resourceList = resources.stream().filter(resource -> resource.getParentResourceNo().equals(root.getResourceNo())).collect(Collectors.toList());

        if (!resourceList.isEmpty()){
            ArrayList<TreeItemVo> treeItemVos = new ArrayList<>();
            resourceList.forEach(resource -> {
                recursionTreeItem(treeItemVos,resource,resources);
            });
            treeItemVo.setChildren(treeItemVos);
        }
        itemVos.add(treeItemVo);
    }

    /**
     * 添加资源
     * @param resourceDto
     */
    @Caching(evict = {@CacheEvict(cacheNames = CacheConstants.LIST,allEntries = true),
            @CacheEvict(cacheNames = CacheConstants.TREE,allEntries = true)})
    @Override
    public void createResource(ResourceDto resourceDto) {
        //属性拷贝
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        //查询父资源
        Resource parenResource = resourceMapper.selectByResourceNo(resourceDto.getParentResourceNo());
        resource.setDataState(parenResource.getDataState());
        //创建当前资源的编号
        boolean isIgnore = true;
        //判断是否是按钮，如果是按钮，则不限制层级
        if(StringUtils.isNotEmpty(resourceDto.getResourceType())
                && resourceDto.getResourceType().equals(SuperConstant.BUTTON)){
            isIgnore=false;
        }

        String resourceNo = createResourceNo(resourceDto.getParentResourceNo(),isIgnore);
        resource.setResourceNo(resourceNo);

        resourceMapper.insert(resource);

    }

    /**
     * 创建资源的编号
     * @param parentResourceNo
     * @return
     */
    private String createResourceNo(String parentResourceNo,boolean isIgnore) {

        //判断资源编号是否大于三级
        //100 001 000 000 000
        //100 001 001 000 000
        //100 001 001 001 000
        //100 001 001 001 001 001
        if(isIgnore && NoProcessing.processString(parentResourceNo).length() / 3 >= 5){
            throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
        }

        //根据父资源编号查询子资源
        ResourceDto dto = ResourceDto.builder().parentResourceNo(parentResourceNo).build();
        List<Resource> resources = resourceMapper.selectList(dto);
        if(EmptyUtil.isNullOrEmpty(resources)){
            //无下属节点，创建新的节点编号  100 001 001 001 000--->100 001 001 001 001
            return NoProcessing.createNo(parentResourceNo,false);
        }else {
            //有下属节点，在已有的节点上追加
            //先获取已有节点的最大值--100001003000000
            Long maxNo = resources.stream().map(resource -> {
                return Long.valueOf(resource.getResourceNo());
            }).max(Comparator.comparing(i -> i)).get();

            return NoProcessing.createNo(String.valueOf(maxNo),true);
        }


    }

    /**
     * 修改资源
     * @param resourceDto
     */
    @Caching(evict = {@CacheEvict(cacheNames = CacheConstants.LIST,allEntries = true),
            @CacheEvict(cacheNames = CacheConstants.TREE,allEntries = true)})
    @Override
    public void updateResource(ResourceDto resourceDto) {
        //转换
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        int flag = resourceMapper.updateByPrimaryKeySelective(resource);
        if (flag == 0) {
            throw new RuntimeException("修改资源信息出错");
        }
    }

    /**
     * 启用禁用
     * @param resourceVo
     * @return
     */
    @Caching(evict = {@CacheEvict(cacheNames = CacheConstants.LIST,allEntries = true),
            @CacheEvict(cacheNames = CacheConstants.TREE,allEntries = true)})
    @Override
    public void isEnable(ResourceVo resourceVo) {
        //启用菜单
        if (resourceVo.getDataState().equals("0")) {
            //判断父级菜单是否是禁用，如果是禁用，不允许启用
            String parentResourceNo = resourceVo.getParentResourceNo();
            Resource parentResource = resourceMapper.selectByResourceNo(parentResourceNo);
            if (parentResource != null && parentResource.getDataState().equals("1")) {
                throw new BaseException(BasicEnum.PARENT_MENU_DISABLE);
            }
        }
        //TODO 当按钮被分配的时候，出错误提示：按钮已分配，不允许禁用   已完成
        int count = roleResourceMapper.checkMenuExistRole(resourceVo.getResourceNo());
        if (resourceVo.getDataState().equals("1") && count > 0) {
            Resource resource = resourceMapper.selectByResourceNo(resourceVo.getResourceNo());
            if(resource.getResourceType().equals("m")){
                throw new BaseException(BasicEnum.MENU_DISTRIBUTED);
            }else {
                throw new BaseException(BasicEnum.BUTTON_DISTRIBUTED);
            }
        }


        //判断是否有子菜单，如果有子菜单，则一起启用或禁用
        String resourceNo = resourceVo.getResourceNo();
        resourceMapper.updateByResourceNo(resourceNo, resourceVo.getDataState());
        resourceMapper.updateByParentResourceNo(NoProcessing.processString(resourceNo), resourceVo.getDataState());
    }

    /**
     * 删除菜单
     * @param resourceNo
     */
    @Caching(evict = {@CacheEvict(cacheNames = CacheConstants.LIST,allEntries = true),
            @CacheEvict(cacheNames = CacheConstants.TREE,allEntries = true)})
    @Override
    public void deleteByResourceNo(String resourceNo) {
        if (hasChildByMenuId(resourceNo)) {
            throw new RuntimeException("存在子菜单,不允许删除");
        }

        //TODO 如果当前菜单被角色引用，则不能删除 已完成
        if (checkMenuExistRole(resourceNo)) {
            throw new RuntimeException("菜单已分配,不允许删除");
        }

        Resource resource = resourceMapper.selectByResourceNo(resourceNo);
        if(resource == null || resource.getDataState().equals(SuperConstant.DATA_STATE_0)){
            throw new RuntimeException("菜单启用状态,不能删除");
        }

        resourceMapper.deleteByResourceNo(resourceNo);
    }

    @Autowired
    private RoleResourceMapper roleResourceMapper;

    /**
     * 查询菜单使用数量
     *
     * @param resourceNo 菜单编号
     * @return 结果
     */
    public boolean checkMenuExistRole(String resourceNo) {
        int result = roleResourceMapper.checkMenuExistRole(resourceNo);
        return result > 0 ? true : false;
    }



    /**
     * 判断是否有子菜单
     * @param resourceNo
     * @return
     */
    private boolean hasChildByMenuId(String resourceNo) {
        int result = resourceMapper.hasChildByMenuId(resourceNo);
        return result > 0 ? true : false;
    }

    /**
     * 我的菜单
     * @param userId
     * @return
     */
    @Override
    public List<MenuVo> menus(Long userId) {

        //查询数据
        List<MenuVo> menuVoList = resourceMapper.selectMenuByUserId(userId);
        if(EmptyUtil.isNullOrEmpty(menuVoList)){
            throw new BaseException(BasicEnum.USER_ROLE_AND_MENU_EMPTY);
        }

        //先给数据分组--->map  {parentNo,[{子菜单},{}]}
        //{"100010002022"：[{"MenuVo：用户管理对象"},{”权限管理”}]}
        //-------------------------------
        //{pid:"100010002022",id:12090909}
        //{pid:"100010002022",id:12090911}
        //{pid:"100010002022",id:12090929}
        //{pid:"100010002022",id:12090939}
        //{100010002022:[{12090909},{12090911},{},{}]}
        Map<String, List<MenuVo>> parentMap = menuVoList.stream().collect(Collectors.groupingBy(MenuVo::getParentResourceNo));
        //遍历原始数据
        menuVoList.forEach(menuVo -> {
            //补全数据
            menuVo.setMeta(MenuMetaVo.builder().title(menuVo.getName()).build());
            menuVo.setRedirect("/"+menuVo.getName());
            //根据当前资源编号查询子资源
            List<MenuVo> childrenList = parentMap.get(menuVo.getResourceNo());
            if(!EmptyUtil.isNullOrEmpty(childrenList)){
                childrenList.forEach(m->{
                    m.setMeta(MenuMetaVo.builder().title(m.getName()).build());
                    m.setRedirect("/"+m.getName());
                });
                menuVo.setChildren(childrenList);
            }
        });

        return parentMap.get(SuperConstant.ROOT_PARENT_ID);
    }
}
